// Bookva is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Jon Peeters, July 2011

import java.io.*;
import java.util.Vector;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.file.*;
 
public class Bookva extends MIDlet implements CommandListener
{
	private final String BOOKS_FILE = "file:///E:/Pgms/livres.txt";
	private final String NEW_BOOKS = "file:///E:/Pgms/nouveaux_livres.txt";
	//private final String BOOKS_FILE = "file:///root1/livres.txt";
	//private final String NEW_BOOKS = "file:///root1/nouveaux_livres.txt";
	private final int RECORD_SIZE = 50;
	private int bytesRead = 0, totalRecords;
	private short booksNb = 0;
	private final String noResult = "Aucun résultat", endResults = "** Fin **";
	private boolean moreResultsAvailable;
	private final String moreResultSep = "....................";
	private long fileSize;
	private short itemsToDisplay = 5, moreToDisplay = 2;
	private FileConnection file, newFile;
	private byte[] recordBuffer = new byte[RECORD_SIZE];
	private InputStream is = null;
	private String searchCmdLabel= "Ok", backCmdLabel = "Retour", quitCmdLabel = "Quitter", moveDownCmdLabel = "++";
	private TextField name;
	private Command searchCmd, backCmd, quitCmd, moveDownCmd;
	private Display display;
	private Form mainForm;
	private List resultsList;

	public Bookva()
	{
		//listRootFiles();
	}

	public void startApp()
	{
		initialize();
		addNewBooks();
	}

	private void initialize ()
	{
		try 
		{
			file = (FileConnection) Connector.open(BOOKS_FILE);
			newFile = (FileConnection) Connector.open(NEW_BOOKS);
			fileSize = file.fileSize();
			if(!file.exists()) 
			{
				throw new IOException("File does not exist");
			}
			display = Display.getDisplay(this);
			mainForm = new Form("Recherche de livres");
			name = new TextField("Nom :", "", 30, TextField.ANY);
			searchCmd = new Command(searchCmdLabel, Command.OK, 2);
			quitCmd = new Command(quitCmdLabel, Command.EXIT, 3);
			mainForm.append(name);
			mainForm.addCommand(searchCmd);
			mainForm.addCommand(quitCmd);
			mainForm.setCommandListener(this);
			backCmd = new Command(backCmdLabel, Command.OK, 1);	
			moveDownCmd = new Command(moveDownCmdLabel, Command.BACK, 0);
			resultsList = new List("Résultats de la recherche", List.IMPLICIT);
			resultsList.setFitPolicy(Choice.TEXT_WRAP_ON);
			resultsList.addCommand(backCmd);
			resultsList.addCommand(moveDownCmd);
			resultsList.setCommandListener(this);
			display.setCurrent(mainForm);		
		}
		catch (Exception ioe)
		{
			ioe.printStackTrace();
		}
	}

	private void addNewBooks() 
	{
		try
		{
			if (!newFile.exists())
			{
				newFile.close();
				return;
			}
			OutputStream outputStream = file.openOutputStream(file.fileSize());
			InputStream in = newFile.openInputStream();	
			String str, output, rec;

			Vector lines = readLines(in);
			if (lines == null)
			{
				in.close();
				outputStream.close();
				newFile.close();
				return;
			}
			int [] buffer;
			int linesNb = lines.size();
			for(int i=0; i<linesNb; i++)
			{
				buffer = (int[]) lines.elementAt(i);
				for (int j=0; j<RECORD_SIZE; j++)
					outputStream.write(buffer[j]);
			}
			Alert alert = new Alert("Nouveau", linesNb + " nouveaux livres ont été ajoutés", null, AlertType.INFO);
			alert.setTimeout(Alert.FOREVER);
			display.setCurrent(alert, mainForm);
			in.close();
			outputStream.close();
			newFile.delete();
			newFile.create();
			newFile.close();
			fileSize = file.fileSize();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	private Vector readLines (InputStream in) throws Exception
	{
		int readChar = in.read();
		if (readChar == -1) 
		{
			return null;
		}

		Vector lines = new Vector();
		int [] buffer = new int [RECORD_SIZE];
		int size = 0;
		while (readChar != -1)
		{
			if (readChar == '\n' || readChar == '\r') 
			{
				if (size > 0 && size <= (RECORD_SIZE-1))
				{
					for (int i=size; i<(RECORD_SIZE-1); i++)
					{
						buffer[i] = ' ';
						size++;
					}
				}
				else
				{
					// (RECORD_SIZE-1) since \n is added after
					System.out.println("SIZE : " + size);
					Alert alert = new Alert("Erreur", "L'enregistrement [" + (lines.size()+1) + "] doit avoir une taille entre 1 et " + (RECORD_SIZE-1), null, AlertType.ERROR);
					alert.setTimeout(Alert.FOREVER);
					display.setCurrent(alert, mainForm);
					return null;
				}
				buffer[size++] = readChar;
				lines.addElement(buffer);
				size = 0;
				buffer = new int [RECORD_SIZE];
			}
			else
			{
				buffer[size++] = readChar;
			}
			readChar = in.read();
		}
		return lines;
	}

	public void commandAction(Command c, Displayable d) 
	{
		String label = c.getLabel();
		if (label == null) return;
		if (label.equals(searchCmdLabel))
		{
			try 	
			{	booksNb = 0;
				moreResultsAvailable = true;
				displayResults(findBooks(name.getString(), false), false); // No more results - Don't append results
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			return;
		}
		if (label.equals(backCmdLabel))
		{
			try { is.close(); is = null; } catch(Exception e) { e.printStackTrace(); }
			display.setCurrent(mainForm);
			return;
		}
		if (label.equals(quitCmdLabel))
		{
			notifyDestroyed();
			return;
		}
		if (label.equals(moveDownCmdLabel))
		{
			// List selection changed
			try { handleMoreResults(); } catch(Exception e) { e.printStackTrace(); }
			return;
		}
	} 

	private void handleMoreResults() throws Exception
	{
		int size = resultsList.size();
		if (size == 0) return;
		displayResults(findBooks(name.getString(), true), true); // more results, append results
	}

	private void displayResults(Vector books, boolean append)
	{
		int bookSize = books.size();
		int lastIndex = resultsList.size()-1, scroll = 0;
		if (append)
		{
			if (bookSize > 0)
			{
				resultsList.append(moreResultSep, null);
				scroll = 2;
			}
		}
		else 
		{
			resultsList.deleteAll();		
		}		
		for (int i=0; i<bookSize; i++)
		{
			resultsList.append(String.valueOf(books.elementAt(i)), null);
		}
		if (!moreResultsAvailable) 
		{ 
			if (booksNb == 0) resultsList.append(noResult, null);
			else 
			{
				resultsList.append(endResults + " [" + booksNb + "/" + totalRecords + "]", null); 
				if (bookSize == 0) scroll = 1;
			}
		}
		if (scroll > 0) resultsList.setSelectedIndex(lastIndex + scroll, true);
		display.setCurrent(resultsList);
	}

	private Vector findBooks (String rgx, boolean more) throws Exception
	{
		if (rgx == null || rgx.length() == 0) 
		{
			return null;
		}

		Vector books = new Vector();
		String rec = null;
		int rgxLength = rgx.length();
		short foundNow = 0, i = 0;
		int j = 0;
		boolean enoughResults = false, matches = true;
		String [] elements = split(rgx, '*');
		int elementsLength = elements.length;		
		int toDisplay = more ? moreToDisplay : itemsToDisplay;
		if (is == null)
		{
			is = file.openInputStream();
			bytesRead = 0;
		}

		while (bytesRead < fileSize && !enoughResults)
		{
			if (is.read(recordBuffer, 0, RECORD_SIZE) == RECORD_SIZE)
			{
				rec = new String(recordBuffer, "ISO-8859-1");
				matches = true;
				i = 0;
				j = 0;
				do
				{
					j = rec.indexOf(elements[i++], j);
					matches &= j >= 0;
				} while (matches && i < elementsLength);
				if (matches)
				{
					books.addElement(rec.trim());
					foundNow++;
					booksNb++;
				}
				bytesRead += RECORD_SIZE;
				enoughResults = foundNow == toDisplay;
			}
			else
			{
				throw new Exception("ERROR reading " + BOOKS_FILE + " (after " + bytesRead + " bytes read)\n! One record is always " + RECORD_SIZE + " bytes !");
			}
		}
		if (bytesRead == fileSize) // Cannot found more results
		{
			is.close();	
			moreResultsAvailable = false;
			totalRecords = bytesRead/RECORD_SIZE;
		}
		else
		{
			moreResultsAvailable = true;
		}
		return books;
	}

	private String [] split (String rgx, char ch)
	{
		String[] result = null;
		int pos = rgx.indexOf(ch);
		if( pos != -1 )
		{
			result = new String[2];
			result[0] = rgx.substring( 0, pos ).trim();
			result[1] = rgx.substring( pos+1 ).trim();
		} 
		else 
		{
		    result = new String [] {rgx};
		}
		return result;
	}

	public void pauseApp()
	{
	}

	public void destroyApp(boolean condition) 
	{
		try
		{
			file.close();
		}
		catch (Exception e)
		{
			System.out.println(e);
		}
    		notifyDestroyed();
   	}

	private void listRootFiles() 
	{
			Enumeration drives = FileSystemRegistry.listRoots();
			StringBuffer buffer = new StringBuffer();
			while (drives.hasMoreElements()) 
			{
				buffer.append((String) drives.nextElement());
				buffer.append('\n');
			}
			Alert alert = new Alert("Liste des fichiers ", String.valueOf(buffer), null, AlertType.INFO);
			alert.setTimeout(Alert.FOREVER);
			mainForm = new Form("Recherche de livres");
			display = Display.getDisplay(this);
			display.setCurrent(alert, mainForm);
	}
}
